/**
 * 虚拟列表组件
 * 用于高效渲染大数据集，只渲染可视区域内的元素
 */
Component({
  options: {
    addGlobalClass: true
  },
  
  properties: {
    // 列表数据源
    dataList: {
      type: Array,
      value: []
    },
    
    // 列表项高度（单位px），如果每项高度不同，则使用预估高度
    itemHeight: {
      type: Number,
      value: 80
    },
    
    // 列表项高度是否固定
    isFixedHeight: {
      type: Boolean,
      value: true
    },
    
    // 预加载的屏幕数量，预加载越多越流畅，但会消耗更多资源
    preloadScreens: {
      type: Number,
      value: 1
    },
    
    // 是否启用下拉刷新
    enablePullRefresh: {
      type: Boolean,
      value: false
    },
    
    // 列表项ID的字段名
    idKey: {
      type: String,
      value: '_id'
    },
    
    // 空列表提示文字
    emptyText: {
      type: String,
      value: '暂无数据'
    },
    
    // 是否显示加载更多
    showLoadMore: {
      type: Boolean,
      value: false
    },
    
    // 是否还有更多数据
    hasMore: {
      type: Boolean,
      value: true
    },
    
    // 加载更多文字
    loadMoreText: {
      type: String,
      value: '加载更多'
    },
    
    // 是否正在加载
    loading: {
      type: Boolean,
      value: false
    }
  },
  
  data: {
    // 可视区域信息
    viewportHeight: 0,
    scrollTop: 0,
    
    // 虚拟列表计算结果
    visibleItems: [], // 当前可见的列表项
    offsetY: 0, // 内容的垂直偏移量
    totalHeight: 0, // 总高度
    
    // 列表项高度缓存
    itemHeightCache: {}, // key: id, value: height
    
    // 下拉刷新状态
    refreshing: false,
    
    // 列表项动态高度测量
    measureQueue: [], // 待测量的列表项ID队列
    isMeasuring: false // 是否正在测量
  },
  
  lifetimes: {
    attached() {
      // 获取可视区域高度
      this._updateViewportInfo();
      
      // 初始化列表
      this._initVirtualList();
      
      // 监听屏幕尺寸变化
      wx.onWindowResize(this._updateViewportInfo.bind(this));
    },
    
    detached() {
      // 取消监听
      wx.offWindowResize();
    }
  },
  
  observers: {
    'dataList, itemHeight': function(dataList, itemHeight) {
      // 重置列表高度缓存（仅当非固定高度且数据变化时）
      if (!this.properties.isFixedHeight && dataList && dataList.length > 0) {
        this._resetHeightCache();
      }
      
      // 重新计算可视区域
      this._initVirtualList();
    },
    
    'scrollTop': function(scrollTop) {
      // 根据滚动位置计算可视区域
      this._calculateVisibleItems(scrollTop);
    }
  },
  
  methods: {
    /**
     * 初始化虚拟列表
     * @private
     */
    _initVirtualList() {
      const { dataList, itemHeight, isFixedHeight } = this.properties;
      
      // 计算总高度
      let totalHeight = 0;
      
      if (isFixedHeight) {
        // 固定高度，简单乘以列表长度
        totalHeight = dataList.length * itemHeight;
      } else {
        // 非固定高度，使用缓存的高度和预估高度计算
        totalHeight = dataList.reduce((height, item) => {
          const id = item[this.properties.idKey];
          const itemHeight = this.data.itemHeightCache[id] || this.properties.itemHeight;
          return height + itemHeight;
        }, 0);
        
        // 添加所有未测量的项到测量队列
        const measureQueue = dataList
          .filter(item => !this.data.itemHeightCache[item[this.properties.idKey]])
          .map(item => item[this.properties.idKey]);
          
        this.setData({ measureQueue });
        
        // 开始测量
        if (measureQueue.length > 0 && !this.data.isMeasuring) {
          this._measureItems();
        }
      }
      
      this.setData({ totalHeight });
      
      // 计算初始可视区域
      this._calculateVisibleItems(this.data.scrollTop);
    },
    
    /**
     * 计算当前可视范围内的列表项
     * @param {Number} scrollTop 滚动位置
     * @private
     */
    _calculateVisibleItems(scrollTop) {
      const { dataList, itemHeight, isFixedHeight, preloadScreens } = this.properties;
      const { viewportHeight, itemHeightCache } = this.data;
      
      if (!dataList || dataList.length === 0 || viewportHeight === 0) {
        this.setData({
          visibleItems: [],
          offsetY: 0
        });
        return;
      }
      
      // 计算可视区域上下边界，包括预加载区域
      const topBoundary = Math.max(0, scrollTop - viewportHeight * preloadScreens);
      const bottomBoundary = scrollTop + viewportHeight + viewportHeight * preloadScreens;
      
      let offsetY = 0; // 内容偏移量
      let currentHeight = 0; // 当前累计高度
      let startIndex = 0; // 开始索引
      let endIndex = 0; // 结束索引
      
      if (isFixedHeight) {
        // 固定高度计算
        startIndex = Math.floor(topBoundary / itemHeight);
        endIndex = Math.min(dataList.length - 1, Math.ceil(bottomBoundary / itemHeight));
        offsetY = startIndex * itemHeight;
      } else {
        // 非固定高度计算
        let i = 0;
        for (; i < dataList.length; i++) {
          const id = dataList[i][this.properties.idKey];
          const height = itemHeightCache[id] || itemHeight;
          
          if (currentHeight >= topBoundary) {
            break;
          }
          
          offsetY = currentHeight;
          startIndex = i;
          currentHeight += height;
        }
        
        currentHeight = offsetY;
        for (; i < dataList.length; i++) {
          const id = dataList[i][this.properties.idKey];
          const height = itemHeightCache[id] || itemHeight;
          
          currentHeight += height;
          endIndex = i;
          
          if (currentHeight >= bottomBoundary) {
            break;
          }
        }
      }
      
      // 确保索引有效
      startIndex = Math.max(0, startIndex);
      endIndex = Math.min(dataList.length - 1, endIndex);
      
      // 提取可视区域内的列表项
      const visibleItems = dataList.slice(startIndex, endIndex + 1);
      
      this.setData({
        visibleItems,
        offsetY
      });
    },
    
    /**
     * 更新可视区域信息
     * @private
     */
    _updateViewportInfo() {
      const query = this.createSelectorQuery();
      query.select('.virtual-list-container').boundingClientRect();
      
      query.exec(res => {
        if (res && res[0]) {
          this.setData({
            viewportHeight: res[0].height
          });
          
          // 重新计算可视区域
          this._calculateVisibleItems(this.data.scrollTop);
        }
      });
    },
    
    /**
     * 重置列表高度缓存
     * @private
     */
    _resetHeightCache() {
      this.setData({
        itemHeightCache: {},
        measureQueue: this.properties.dataList.map(item => item[this.properties.idKey]),
        isMeasuring: false
      });
    },
    
    /**
     * 测量列表项高度
     * @private
     */
    _measureItems() {
      const { measureQueue } = this.data;
      
      if (measureQueue.length === 0) {
        this.setData({ isMeasuring: false });
        return;
      }
      
      this.setData({ isMeasuring: true });
      
      // 每次测量前5个项目，减少重绘压力
      const batchSize = 5;
      const currentBatch = measureQueue.slice(0, batchSize);
      
      // 等待视图渲染
      setTimeout(() => {
        const query = this.createSelectorQuery();
        
        currentBatch.forEach(id => {
          query.select(`#item-${id}`).boundingClientRect();
        });
        
        query.exec(res => {
          if (!res || res.length === 0) {
            // 没有结果，可能还没渲染完成，延迟重试
            setTimeout(() => this._measureItems(), 50);
            return;
          }
          
          // 更新高度缓存
          const itemHeightCache = { ...this.data.itemHeightCache };
          
          res.forEach((rect, index) => {
            if (rect) {
              const id = currentBatch[index];
              itemHeightCache[id] = rect.height;
            }
          });
          
          // 更新队列，移除已测量的项
          const remainingQueue = this.data.measureQueue.filter(id => !currentBatch.includes(id));
          
          this.setData({
            itemHeightCache,
            measureQueue: remainingQueue
          });
          
          // 重新计算总高度
          this._updateTotalHeight();
          
          // 继续测量剩下的项
          if (remainingQueue.length > 0) {
            setTimeout(() => this._measureItems(), 50);
          } else {
            this.setData({ isMeasuring: false });
          }
        });
      }, 50);
    },
    
    /**
     * 更新列表总高度
     * @private
     */
    _updateTotalHeight() {
      const { dataList } = this.properties;
      const { itemHeightCache } = this.data;
      
      // 使用已知高度和预估高度计算总高度
      const totalHeight = dataList.reduce((height, item) => {
        const id = item[this.properties.idKey];
        const itemHeight = itemHeightCache[id] || this.properties.itemHeight;
        return height + itemHeight;
      }, 0);
      
      this.setData({ totalHeight });
      
      // 重新计算可视区域
      this._calculateVisibleItems(this.data.scrollTop);
    },
    
    /**
     * 滚动事件处理
     * @param {Object} e 事件对象
     */
    handleScroll(e) {
      const { scrollTop } = e.detail;
      
      this.setData({ scrollTop });
      
      // 触发滚动事件
      this.triggerEvent('scroll', {
        scrollTop,
        isReachBottom: this._isReachBottom(scrollTop)
      });
      
      // 检查是否触底
      if (this._isReachBottom(scrollTop) && !this.data.loading && this.properties.hasMore) {
        this.triggerEvent('loadmore');
      }
    },
    
    /**
     * 检查是否触底
     * @param {Number} scrollTop 当前滚动位置
     * @returns {Boolean} 是否触底
     * @private
     */
    _isReachBottom(scrollTop) {
      const { viewportHeight, totalHeight } = this.data;
      // 滚动到底部前预加载区域的大小（默认提前100px触发）
      const preloadDistance = 100;
      
      return scrollTop + viewportHeight + preloadDistance >= totalHeight;
    },
    
    /**
     * 下拉刷新开始
     */
    handlePullRefresh() {
      if (this.data.refreshing || !this.properties.enablePullRefresh) return;
      
      this.setData({ refreshing: true });
      this.triggerEvent('refresh');
    },
    
    /**
     * 完成刷新
     */
    finishRefresh() {
      this.setData({ refreshing: false });
    },
    
    /**
     * 点击列表项
     * @param {Object} e 事件对象
     */
    handleItemTap(e) {
      const { index, id } = e.currentTarget.dataset;
      
      this.triggerEvent('itemtap', {
        item: this.data.visibleItems[index],
        index,
        id
      });
    },
    
    /**
     * 滚动到指定索引
     * @param {Number} index 目标索引
     * @param {Number} offset 偏移量（单位px）
     */
    scrollToIndex(index, offset = 0) {
      const { dataList, isFixedHeight, itemHeight } = this.properties;
      const { itemHeightCache } = this.data;
      
      if (index < 0 || index >= dataList.length) return;
      
      let position = 0;
      
      if (isFixedHeight) {
        // 固定高度直接计算
        position = index * itemHeight;
      } else {
        // 非固定高度累加计算
        for (let i = 0; i < index; i++) {
          const id = dataList[i][this.properties.idKey];
          position += itemHeightCache[id] || itemHeight;
        }
      }
      
      // 加上偏移量
      position += offset;
      
      // 执行滚动
      wx.createSelectorQuery()
        .in(this)
        .select('.virtual-list-container')
        .node()
        .exec(res => {
          const scrollView = res[0].node;
          scrollView.scrollTo({ top: position, behavior: 'smooth' });
        });
    },
    
    /**
     * 重置组件状态
     */
    reset() {
      this.setData({
        scrollTop: 0,
        offsetY: 0,
        refreshing: false
      });
      
      // 重新计算可视区域
      this._calculateVisibleItems(0);
    }
  }
}); 